﻿// License: The MIT License (MIT) Copyright (c) 2012 Barend Gehrels

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;

namespace GrooveSharp
{
    // In the past I used the KeyboardListener (Ownskit.Utils). 
    // But this caused a Kaspersky warning.
    // Probably every hook will do that.
    // So we implement our own, based on a timer.
    // It does not heavily influence processing time, if not many keys are specified
    // It does not implement repeating behaviour (holding key)
    // Only use this class to intercept keys if main window does NOT have the focus. 
    // If it HAS the focus, then use PreviewKeyDown
    // Do not specify too many keys
    public class KeyboardWatcher
    {
        public class KeyboardEventArgs : EventArgs
        {
            private System.Windows.Input.Key m_key;

            public System.Windows.Input.Key Key { get { return m_key; } }

            public KeyboardEventArgs(System.Windows.Input.Key key)
            {
                m_key = key;
            }
        }

        public delegate void KeyboardWatcherEventHandler(object sender, KeyboardEventArgs args);

        private System.Windows.Threading.DispatcherTimer m_KeyboardTimer = new System.Windows.Threading.DispatcherTimer();
        private System.Windows.Input.Key[] m_keys;

        public event KeyboardWatcherEventHandler KeyDown;


        // Initialize to null / not possible, then to weird/antique key
        // http://www.autohotkey.com/forum/topic39927.html
        // Note that even if this antique key is pressed, the class will function normally.
        // Only if EraseEof is really specified for interception, it will work (a bit) weird (on/off/on/off)
        const System.Windows.Input.Key NULL_KEY = System.Windows.Input.Key.EraseEof; 

        private bool m_isDown = false;
        private System.Windows.Input.Key m_lastKey = NULL_KEY;

        public KeyboardWatcher(System.Windows.Input.Key[] keys)
        {
            if (keys.Length > 10)
            {
                throw new ArgumentOutOfRangeException("Too many keys specified");
            }

            m_keys = keys;
            m_KeyboardTimer.Interval = TimeSpan.FromMilliseconds(10);
            m_KeyboardTimer.Tick += new EventHandler(keyboardTimer_Tick);
            m_KeyboardTimer.Start();
        }

        private void keyboardTimer_Tick(object sender, EventArgs e)
        {
            if (KeyDown != null)
            {
                if (! m_isDown)
                {
                    foreach (var key in m_keys)
                    {
                        if (System.Windows.Input.Keyboard.IsKeyDown(key))
                        {
                            m_isDown = true;
                            m_lastKey = key;
                            KeyDown(this, new KeyboardEventArgs(key));
                            return;
                        }
                    }
                }
                else if (!System.Windows.Input.Keyboard.IsKeyDown(m_lastKey))
                {
                    m_isDown = false;
                    m_lastKey = NULL_KEY;
                }
            }
        }

    }
}
